// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "media/cast/net/rtp/rtp_parser.h"

#include "base/big_endian.h"
#include "base/logging.h"
#include "media/cast/constants.h"
#include "media/cast/net/rtp/rtp_defines.h"

namespace media {
namespace cast {

    // static
    bool RtpParser::ParseSsrc(const uint8_t* packet,
        size_t length,
        uint32_t* ssrc)
    {
        base::BigEndianReader big_endian_reader(
            reinterpret_cast<const char*>(packet), length);
        return big_endian_reader.Skip(8) && big_endian_reader.ReadU32(ssrc);
    }

    RtpParser::RtpParser(uint32_t expected_sender_ssrc,
        uint8_t expected_payload_type)
        : expected_sender_ssrc_(expected_sender_ssrc)
        , expected_payload_type_(expected_payload_type)
        , last_parsed_frame_id_(FrameId::first() - 1)
    {
    }

    RtpParser::~RtpParser() { }

    bool RtpParser::ParsePacket(const uint8_t* packet,
        size_t length,
        RtpCastHeader* header,
        const uint8_t** payload_data,
        size_t* payload_size)
    {
        DCHECK(packet);
        DCHECK(header);
        DCHECK(payload_data);
        DCHECK(payload_size);

        if (length < (kRtpHeaderLength + kCastHeaderLength))
            return false;

        base::BigEndianReader reader(reinterpret_cast<const char*>(packet), length);

        // Parse the RTP header.  See
        // http://en.wikipedia.org/wiki/Real-time_Transport_Protocol for an
        // explanation of the standard RTP packet header.
        uint8_t bits;
        if (!reader.ReadU8(&bits))
            return false;
        const uint8_t version = bits >> 6;
        if (version != 2)
            return false;
        header->num_csrcs = bits & kRtpNumCsrcsMask;
        if (bits & kRtpExtensionBitMask)
            return false; // We lack the implementation to skip over an extension.
        if (!reader.ReadU8(&bits))
            return false;
        header->marker = !!(bits & kRtpMarkerBitMask);
        header->payload_type = bits & ~kRtpMarkerBitMask;
        if (header->payload_type != expected_payload_type_)
            return false; // Punt: Unexpected payload type.
        uint32_t truncated_rtp_timestamp;
        if (!reader.ReadU16(&header->sequence_number) || !reader.ReadU32(&truncated_rtp_timestamp) || !reader.ReadU32(&header->sender_ssrc) || header->sender_ssrc != expected_sender_ssrc_) {
            return false;
        }
        header->rtp_timestamp = last_parsed_rtp_timestamp_.Expand(truncated_rtp_timestamp);

        // Parse the Cast header.  Note that, from the RTP protocol's perspective, the
        // Cast header is part of the payload (and not meant to be an extension
        // header).
        if (!reader.ReadU8(&bits))
            return false;
        header->is_key_frame = !!(bits & kCastKeyFrameBitMask);
        header->is_reference = !!(bits & kCastReferenceFrameIdBitMask);
        uint8_t truncated_frame_id;
        if (!reader.ReadU8(&truncated_frame_id) || !reader.ReadU16(&header->packet_id) || !reader.ReadU16(&header->max_packet_id)) {
            return false;
        }
        // Sanity-check: Do the packet ID values make sense w.r.t. each other?
        if (header->max_packet_id < header->packet_id)
            return false;
        uint8_t truncated_reference_frame_id;
        if (!header->is_reference) {
            // By default, a key frame only references itself; and non-key frames
            // reference their direct predecessor.
            truncated_reference_frame_id = truncated_frame_id;
            if (!header->is_key_frame)
                --truncated_reference_frame_id;
        } else if (!reader.ReadU8(&truncated_reference_frame_id)) {
            return false;
        }

        header->num_extensions = bits & kCastExtensionCountmask;
        for (int i = 0; i < header->num_extensions; i++) {
            uint16_t type_and_size;
            if (!reader.ReadU16(&type_and_size))
                return false;
            base::StringPiece tmp;
            if (!reader.ReadPiece(&tmp, type_and_size & 0x3ff))
                return false;
            base::BigEndianReader chunk(tmp.data(), tmp.size());
            switch (type_and_size >> 10) {
            case kCastRtpExtensionAdaptiveLatency:
                if (!chunk.ReadU16(&header->new_playout_delay_ms))
                    return false;
            }
        }

        last_parsed_rtp_timestamp_ = header->rtp_timestamp;

        header->frame_id = last_parsed_frame_id_.Expand(truncated_frame_id);
        header->reference_frame_id = header->frame_id.Expand(truncated_reference_frame_id);
        last_parsed_frame_id_ = header->frame_id;

        // All remaining data in the packet is the payload.
        *payload_data = reinterpret_cast<const uint8_t*>(reader.ptr());
        *payload_size = reader.remaining();

        return true;
    }

} // namespace cast
} // namespace media
